/***************************************************************************//**
 * @file mac_timeSync.h
 * @brief for mac time synchronized, use BURTC timer
 * @author ze .
 * @date  2022/7/03.
 * @copyright Copyright 
 ******************************************************************************/
#include "mac_timeSync.h"
#include "softwareMultiTimer.h"
#include "mac_fhss.h"
#include "mac_receiver.h"
#include "mac_transmitter.h"
#include "mac_TDMA.h"
#include "mac_packetHandle.h"
#include "device_type.h"
#include "bsp_rf.h"

#define WRITE_BU_RTC_COUNT(a)  setBURTCCount(a) //To Do 待定义，设置U32

#ifdef RFM_RTC_DISABLE //所有低功耗定时器也将无法使用
static uint8_t  isRtcEnabled = 0;//心跳关闭，即RTC可不用使能，如何判断电池欠压？
#else
static uint8_t  isRtcEnabled = 1;//心跳开启，即RTC开启
#endif
static uint8_t  isSyncBcastReq = 1; //默认置1，若Callback函数内 未组包，则取消发送

#ifdef HUB_DEVICE
static uint8_t isTimeSyncedFlag = 1; //HUB默认时间是同步的
#else
static uint8_t isTimeSyncedFlag = 0; //ToDo 当失去与主机通信时，清0！ 无RTC设备，该标志默认清0
#endif

static uint8_t isNextSyncBcastNeeded = 0;
static uint8_t isNextRxWakeNeeded = 0;
static uint8_t isNextSpvnNeeded = 0;
static uint8_t isNextFileTransNeeded = 0;
static uint8_t isNextMainChanUpdateNeeded = 0;
// 无线接收完成事件时间戳
static uint32_t Trx;

// use  EN_LP_HARD_TIMER_EM4_BU_RTC
static MultiTimer stSyncBcastTimer =
{
    .Handle = 0xA1,
};

// use  EN_LP_HARD_TIMER_EM4_BU_RTC
static MultiTimer stRxWakeTimer =
{
    .Handle = 0xA2,
};

static MultiTimer stSpvnTimer =
{
    .Handle = 0xA3,
};

static MultiTimer stFileTransTimer =
{
    .Handle = 0xA4,
};

// use  EN_LP_HARD_TIMER_EM4_BU_RTC
static MultiTimer stMainChanUpdateTimer=
{
    .Handle = 0xB1,
};

#ifdef SPVN_DISABLE
// use  EN_LP_HARD_TIMER_EM2_SYS_RTC
static MultiTimer stLoseSyncForSpvnDisableTimer =
{
    .Handle = 0xC1,
};

void loseSyncForSpvnDisableTimerExpireCallback(MultiTimer* timer, void* userData)
{
   clearTimeSyncedFlag();
}
#endif

static void updateRecvAlwaysOnMainChan()
{
  if(!isTimeSynced())
  {
      clearHopOnflag();
  }
//debug1123,该EN_RECV_REQ_FILE_DATA_RX_CHAN事件由stFileTransTimer更新 开启和关闭
//  if(getMacRecvEventBitMask() & (GET_DW_BIT_MASK(EN_RECV_REQ_FILE_DATA_RX_CHAN) ))//处于常收状态，不同的常收模式，可使用不同的跳频序列偏置
//  {
//      setMacRecvEvent(EN_RECV_REQ_FILE_DATA_RX_CHAN, getAccessChan(CHAN_TYPE_MAIN));
//  }
  if(getMacRecvEventBitMask() & (GET_DW_BIT_MASK(EN_RECV_REQ_ALWAYS_ON_CHAN) ))//处于常收状态
  {
      setMacRecvEvent(EN_RECV_REQ_ALWAYS_ON_CHAN, getAccessChan(CHAN_TYPE_MAIN));
  }
}

void mainChanUpdateTimerExpireCallback(MultiTimer* timer, void* userData)
{
   isNextMainChanUpdateNeeded = 1;
   updateRecvAlwaysOnMainChan();
}

void syncBcastTimerExpireCallback(MultiTimer* timer, void* userData)
{
  isNextSyncBcastNeeded = 1;
  if(isSyncBcastReq)
  {
    //isSyncBcastReq = 0; //debug 暂时屏蔽
    setMacTransEvent(EN_TRANS_REQ_SYNC_BCAST, 0xFF,0 ,0);
  }
}

void rxWakeTimerExpireCallback(MultiTimer* timer, void* userData)
{
  isNextRxWakeNeeded = 1;
  setMacRecvEvent(EN_RECV_REQ_SYNC_RXWAKE_WINDOW, getAccessChan(CHAN_TYPE_MAIN)); //ToDo getAccessChan 函数执行耗时，有乘除法
}

void fileTransTimerExpireCallback(MultiTimer* timer, void* userData)
{
  isNextFileTransNeeded = 1;
  //根据发送者 或 接收者的角色 而定
  //setMacRecvEvent(EN_RECV_REQ_SYNC_RXWAKE_WINDOW, getAccessChan(CHAN_TYPE_MAIN)); //ToDo getAccessChan 函数执行耗时，有乘除法
}

void spvnTimerExpireCallback(MultiTimer* timer, void* userData)
{
  isNextSpvnNeeded = 1;
  setMacTransEvent(EN_TRANS_REQ_SPVN, 0x00, ADHOC_RETRY_COUNT_MAX ,LBT_REDO_COUNT_MAX);
}

//当进入常接收模式时，需打开MainChanUpdatetimer
void startAlwaysRxMainChanUpdateTimer(uint8_t isFirstlyStart)
{
  uint64_t temp64;
  //TODO 做个极限测试
  temp64 = (getSoftwareTimerTick(EN_LP_HARD_TIMER_EM4_BU_RTC).dlSysTick & 0xFFFFFFFFFFFF0000) + getNextMainChanUpdateTimerDeadLine(); //改为| 单独考虑65536情况
  softwareMultiTimeDeadlineStart(EN_LP_HARD_TIMER_EM4_BU_RTC,&stMainChanUpdateTimer, temp64, mainChanUpdateTimerExpireCallback, NULL ,isFirstlyStart);//进入该状态时 打开定时器计超时
  //printf("-)%u\r\n",(uint32_t)temp64);
}

//当退出常接收模式时，关闭MainChanUpdatetimer
void stopAlwaysRxMainChanUpdateTimer()
{
  isNextMainChanUpdateNeeded = 0;
  softwareMultiTimerStop(EN_LP_HARD_TIMER_EM4_BU_RTC,&stMainChanUpdateTimer,0);//停止计时
}

//当角色切換切换为网关时 需开启syncBcast timer
void startSyncBcastTimer(uint8_t isFirstlyStart)
{
  uint64_t temp64;

  temp64 = (getSoftwareTimerTick(EN_LP_HARD_TIMER_EM4_BU_RTC).dlSysTick & 0xFFFFFFFFFFFF0000) + 66355;//66355 = 65536(2s) + 819(25ms) 开启下一次
  softwareMultiTimeDeadlineStart(EN_LP_HARD_TIMER_EM4_BU_RTC,&stSyncBcastTimer, temp64, syncBcastTimerExpireCallback, NULL ,isFirstlyStart);//进入该状态时 打开定时器计超时
}

//当角色为RFM子节点时 且RxWanke打开时 需开启RxWake timer,时间同步后续重新打开？ ToDo
void startRxWakeTimer(uint8_t isFirstlyStart)
{
  uint64_t temp64;
  #if 0 //重新时间同步后，可能存在错过广播窗口的风险
    temp64 = (getSoftwareTimerTick(EN_LP_HARD_TIMER_EM4_BU_RTC).dlSysTick & 0xFFFFFFFFFFFF0000) + 67174;//67174 = 65536(2s) + 1638(50ms)
  #else
    temp64 = getSoftwareTimerTick(EN_LP_HARD_TIMER_EM4_BU_RTC).dlSysTick;
    if((uint64_t)temp64 < 1637)
        temp64 = (temp64 & 0xFFFFFFFFFFFF0000) + 1638;
    else
        temp64 = (temp64 & 0xFFFFFFFFFFFF0000) + 67174;//67174 = 65536(2s) + 1638(50ms)
  #endif
  softwareMultiTimeDeadlineStart(EN_LP_HARD_TIMER_EM4_BU_RTC,&stRxWakeTimer, temp64,rxWakeTimerExpireCallback, NULL ,isFirstlyStart);//进入该状态时 打开定时器计超时
  if(get_mac_printf_log())
    printf("<%u",(uint32_t)temp64);
}
//当RxWake 关闭时，关闭该功能
void stopRxWakeTimer()
{
  isNextRxWakeNeeded = 0;
  softwareMultiTimerStop(EN_LP_HARD_TIMER_EM4_BU_RTC,&stRxWakeTimer,0);//进入该状态时 打开定时器计超时
}

//当角色为RFM子节点时 且RxWanke打开时 需开启RxWake timer,时间同步后续重新打开？ ToDo
void startFileTransTimer(uint8_t isFirstlyStart)
{
  uint64_t temp64;
  temp64 = (getSoftwareTimerTick(EN_LP_HARD_TIMER_EM4_BU_RTC).dlSysTick & 0xFFFFFFFFFFFF0000) ;//+ ;
  softwareMultiTimeDeadlineStart(EN_LP_HARD_TIMER_EM4_BU_RTC,&stFileTransTimer, temp64,fileTransTimerExpireCallback, NULL ,isFirstlyStart);//进入该状态时 打开定时器计超时
  if(get_mac_printf_log())
    printf("<-%u",(uint32_t)temp64);
}
//当RxWake 关闭时，关闭该功能
void stopFileTransTimer(uint8_t isFirstlyStart)
{
  isNextFileTransNeeded = 0;
  softwareMultiTimerStop(EN_LP_HARD_TIMER_EM4_BU_RTC,&stFileTransTimer,0);//进入该状态时 打开定时器计超时
}


//当角色为RFM子节点时 且RxWanke打开时 需开启RxWake timer,时间同步后续重新打开？ ToDo
void startSpvnTimer(uint8_t isFirstlyStart)
{
  uint64_t temp64;
  uint16_t spvnTime;//单位1/32768
  uint32_t deadLine;
  uint32_t temp32;
  getNextSpvnDeadline(&spvnTime,&deadLine);
  temp32 = spvnTime << 15;
  temp64 = getSoftwareTimerTick(EN_LP_HARD_TIMER_EM4_BU_RTC).dlSysTick;
  //printf("S,%u,%u,%u",temp32 ,deadLine ,(uint32_t)temp64);
  //temp64 = temp64 - ((uint32_t)temp64 % temp32) + deadLine; //需确保temp32整除temp64的同步最大范围时间
  temp64 = temp64 - (temp64 % temp32) + deadLine; //需确保temp32整除temp64的同步最大范围时间,bug 0219，去掉temp64强制转换U32，避免除数非2幂次方时异常
  if(temp64 < getSoftwareTimerTick(EN_LP_HARD_TIMER_EM4_BU_RTC).dlSysTick)
  {
      temp64 += temp32;
  }

  if(isFirstlyStart && (temp64 < (getSoftwareTimerTick(EN_LP_HARD_TIMER_EM4_BU_RTC).dlSysTick + 0x20000)) ) //若首次开启，仅剩4s发心跳，则延迟到下一个周期
  {
      temp64 += temp32;
  }

  softwareMultiTimeDeadlineStart(EN_LP_HARD_TIMER_EM4_BU_RTC, &stSpvnTimer, temp64, spvnTimerExpireCallback, NULL ,isFirstlyStart);//进入该状态时 打开定时器计超时
  if(get_mac_printf_log())
  printf("S<%u",(uint32_t)temp64);
}
//当RxWake 关闭时，关闭该功能
void stopSpvnTimer()
{
  isNextSpvnNeeded = 0;
  softwareMultiTimerStop(EN_LP_HARD_TIMER_EM4_BU_RTC,&stSpvnTimer,0);//进入该状态时 打开定时器计超时
}

void timeSyncEventCheck()
{
  if(isNextMainChanUpdateNeeded)
  {
      isNextMainChanUpdateNeeded = 0;
      startAlwaysRxMainChanUpdateTimer(0);
      updateRecvAlwaysOnMainChan();//开启定时器后 再次更新主信道，确保信道值为最新的 正确的
  }
  if(isNextSyncBcastNeeded)
  {
      isNextSyncBcastNeeded = 0; //此处不清0，会卡死，可继续debug.printf卡死
      startSyncBcastTimer(0);
  }
  if(isNextRxWakeNeeded && isTimeSynced()) //新增，仅当时间同步才开启RxWake，标志位未清 每次MCU唤醒时均会处理
  {
      isNextRxWakeNeeded = 0;
      startRxWakeTimer(0);
  }
  if(isNextSpvnNeeded)
  {
      isNextSpvnNeeded = 0;
      startSpvnTimer(0);
  }
  if(isNextFileTransNeeded)
  {
      isNextFileTransNeeded = 0;
      startFileTransTimer(0);
  }

}


void setRxTimeStamp(uint32_t rxTime)
{
    Trx = rxTime;
}
/**
 * 
 * @brief 时间同步接口，由接收处理侧调用,仅从设备会调用该接口。
 * 
 * @param Thub HUB 对端发送时间戳，24bit 512s周期时间；校准前先disable compare interrupt. 针对BURTC进行
 * @param t_packet 报文的持续时间，前导码+同步字+Payload+CRC长 * 8 / bps * 32768(<<实现)
 * @param Trx     本地RFM接收中断的本地时间戳；
 * @return int 0: 误差值, 可正可负； 对端-本地的值
 */
int doTimeSyncToHub(uint32_t Thub, uint32_t t_packet)
{
    // 上次同步时刻的时间戳，以及同步的误差
    static uint32_t s_dwLastSyncTimeStamp = 0;
    static int32_t s_iLastCtuneCalErrorRate = 0; //单位PPM
    uint8_t  symbol = 0; //0表示+ ， 1表示-
    uint32_t dwErrorValue ;
    uint32_t temp32 ;
    int32_t tempi;
    //Trfm=Thub+ t1 +t_packet +(Thandle-Trx)； 
    dwErrorValue = ( Thub + (uint32_t)HUB_TRANS_TO_RFM_T1 + t_packet + (uint32_t)RFM_RF_INTERRUPT_DELAY - (Trx & 0x00FFFFFF) ) & 0x00FFFFFF;
    if(dwErrorValue >= 0x7FFFFF) //负数判断，当为此值时，认为负数，正负各一半概率
    {
      temp32 = 0x1000000 - dwErrorValue;// 此为补码的原理，考虑为24位数
      if(Trx > temp32)//若当前Trx-temp32>0 则可以减之，避免当前BuRTc值太小 被减后 翻转
      {
          symbol = 1;
          dwErrorValue = temp32;
      }
    }

    //if( dwErrorValue< RFM_HUB_SYNC_ERROR_MID_TOLERANCE) //只要是收到主机的应答 均认为已时间同步
    {
        if(isRtcEnabled)//ToDo 无心跳设备，若RTC使能 则可在延时一段时间后自动将该标志清除！
        {
           isTimeSyncedFlag = 1; //同步误差缩小，故完成时间同步
        }

#ifdef SPVN_DISABLE
        softwareMultiTimerStart(EN_LP_HARD_TIMER_EM3_SYS_RTC, &stLoseSyncForSpvnDisableTimer, 39321600, loseSyncForSpvnDisableTimerExpireCallback, NULL ,1);//20min，重复进入打断刷新
#endif
    }

    temp32 = READ_BU_RTC_COUNT() - s_dwLastSyncTimeStamp; //当前校准时间，可与 s_dwLastSyncTimeStamp 相减 得出变异率；
    if( dwErrorValue >= RFM_HUB_SYNC_ERROR_MID_TOLERANCE || temp32 >= RFM_HUB_SYNC_CAL_TIME_INTERVAL_MIN)//时间误差较大Mid值 或者 上次校准时间超过16s
    {
#ifdef RFM_DEVICE
        /*******1st，误差较大，先需停止现有定时器，再进行同步,并重新开始 该组定时器**********/
        if(stSyncBcastTimer.status != EN_MULTITIMER_STATUS_IDLE)
        {
          isNextSyncBcastNeeded = 1;
        }
        if(stRxWakeTimer.status != EN_MULTITIMER_STATUS_IDLE)
        {
          isNextRxWakeNeeded = 1;
        }
        if(stSpvnTimer.status != EN_MULTITIMER_STATUS_IDLE)
        {
          isNextSpvnNeeded = 1;
        }
        if(stMainChanUpdateTimer.status != EN_MULTITIMER_STATUS_IDLE)
        {
          isNextMainChanUpdateNeeded = 1;
        }
        if(stFileTransTimer.status != EN_MULTITIMER_STATUS_IDLE)
        {
          isNextFileTransNeeded = 1;
        }

        softwareMultiTimerStopAll(EN_LP_HARD_TIMER_EM4_BU_RTC);

        if(symbol)
        {
          WRITE_BU_RTC_COUNT(READ_BU_RTC_COUNT() - dwErrorValue + 2); //2st。进行时间同步，暂仅校准偏差，此处翻转的风险？无？ 基本每次同步，都需要重新开启BURTC
        }//+2 考虑 读取count 和 写 count的滞后效应
        else
        {
          WRITE_BU_RTC_COUNT(READ_BU_RTC_COUNT() + dwErrorValue + 2); //2st。进行时间同步，暂仅校准偏差，此处翻转的风险？无？ 基本每次同步，都需要重新开启BuRTc定时器任务！
        }

        //ToDo。估计频偏，并步进校准 dwErrorValue * 100 0000 / temp32 ,4000为防越界值
        if(s_dwLastSyncTimeStamp != 0 && dwErrorValue < 4000)//上次校准值,和当前频偏值若合法
        {
          if(symbol)
          {
              tempi = 0 - (int32_t)( dwErrorValue*1000000 / temp32);
          }
          else
          {
              tempi = (dwErrorValue*1000000 / temp32 ); //单位PPM
          }
          if(get_mac_printf_log())
            printf("%ldP",tempi);
          if(tempi < -1) //HUB较RFM慢，即RFM相对HUB快，正偏，频率需调小 即Cp变大
          {
              setRtcXOCtuneUp();
              s_iLastCtuneCalErrorRate = tempi;
          }
          else if(tempi > 1)//HUB较RFM快，即RFM相对HUB慢，负偏， 频率需调大，即Cp变小
          {
              setRtcXOCtuneDown();//ToDo 何时停止？何时启动？
              s_iLastCtuneCalErrorRate = tempi;
          }

        }
        s_dwLastSyncTimeStamp = READ_BU_RTC_COUNT();
        if(s_dwLastSyncTimeStamp == 0)
          s_dwLastSyncTimeStamp++;

        if(get_mac_printf_log())
          printf("'");

        BURTC_SyncWait(); //确保setCount指令被执行，在setRtcCount后必须调用，在读count之前
        /**********3st， 重开该组定时器，校准时 高位的准度会损失，0xFF000000！***************/
        timeSyncEventCheck();
//        startRxWakeTimer(0);
//        startSpvnTimer(0);

#endif
    }

    //To Do ,重新开始所有时分多址相关即使用BURTC的定时器任务
    printf("@[%c%u]\r\n",(symbol)?'-':'+', dwErrorValue);

    return(dwErrorValue);
}

/**
 * 
 * @brief 时间同步接口，由接收处理侧调用,评估时间同步误差
 * 
 * @param Trfm RFM发送时间戳，24bit 512s周期时间；校准前先disable compare interrupt. 针对BURTC进行
 * @param t_packet 报文的持续时间，前导码+同步字+Payload+CRC长 * 8 / bps * 32768(<<实现)
 * @param Trx     HUB接收中断的本地时间戳；
 * @return int 0: 误差值, 可正可负；
 */
int evaluaTimeSyncError(uint32_t Trfm, uint32_t t_packet)
{
    uint32_t dwErrorValue = 0;
    uint8_t  symbol = 0 ; //0表示+ ， 1表示-

    isTimeSyncedFlag = 1; //ToDo 通信成功后，即表明已被同步
    //Trfm=Thub+ t1 +t_packet +(Thandle-Trx)； 
    dwErrorValue = ( Trfm + (uint32_t)RFM_TRANS_TO_HUB_T1 + t_packet + (uint32_t)HUB_RF_INTERRUPT_DELAY - (Trx & 0x00FFFFFF) ) & 0x00FFFFFF;
    if(dwErrorValue >= 0x7FFFFF) //当为此值时，认为负数，正负各一半概率
    {
      dwErrorValue = 0x1000000 - dwErrorValue;//
      symbol = 1;
    }

    if( dwErrorValue< RFM_HUB_SYNC_ERROR_MID_TOLERANCE  )
    {

    }

    printf("@[%c%u]\r\n",(symbol)?'-':'+', dwErrorValue);

}

//afcErr 单位Hz
void rfHfxoSyncToHub(int afcErr)
{
#ifdef RFM_DEVICE
uint32_t temp32 = 0x3C; //默认值
if(afcErr < -500)
  {
    temp32 = bsp_rf_get_ctune();
    if(temp32 < 250)//增加边界
    {
      temp32++;
      bsp_rf_set_ctune(temp32);
      if(get_mac_printf_log())
        printf("+H%d\r\n",temp32);
    }
  }
else if(afcErr > 500)
  {
    temp32 = bsp_rf_get_ctune();
    if(temp32 > 2)//增加边界
    {
      temp32--;
      bsp_rf_set_ctune(temp32);
      if(get_mac_printf_log())
        printf("-H%d\r\n",temp32);
    }
  }
#endif
}


void timeSyncToHub()
{
  uint32_t temp32;
  uint8_t* pPacketData;
  pPacketData = getLastRecvPacketPoint()->start_of_packet;
  temp32 = pPacketData[5+MAC_LAYER_PACKET_SYNC_TIME_OFFSET];
  temp32 = temp32 << 8;
  temp32 += pPacketData[6+MAC_LAYER_PACKET_SYNC_TIME_OFFSET];
  temp32 = temp32 << 8;
  temp32 += pPacketData[7+MAC_LAYER_PACKET_SYNC_TIME_OFFSET];

  doTimeSyncToHub(temp32,48+((pPacketData[0]+3)*6826/1000));//40bit前导码长度+同步字长度 + 报文负载长度+CRC长度
  //do hfxo ctune, just rfm needed
#ifdef RF_HFXO_AFC_CTUNE_CALIBRATE_ENABLE
  rfHfxoSyncToHub(getLastRecvPacketPoint()->AfcError);
#endif
}

void calcRfmTimeSyncError()
{
  uint32_t temp32;
  uint8_t* pPacketData;
  pPacketData = getLastRecvPacketPoint()->start_of_packet;
  temp32 = pPacketData[5+MAC_LAYER_PACKET_SYNC_TIME_OFFSET];
  temp32 = temp32 << 8;
  temp32 += pPacketData[6+MAC_LAYER_PACKET_SYNC_TIME_OFFSET];
  temp32 = temp32 << 8;
  temp32 += pPacketData[7+MAC_LAYER_PACKET_SYNC_TIME_OFFSET];

  evaluaTimeSyncError(temp32,48+((pPacketData[0]+3)*6826/1000));//40bit前导码长度+同步字长度 + （报文负载长度+CRC长度） * 8 / 38.4k * 32768
}

uint8_t isTimeSynced(void)
{
  return isTimeSyncedFlag;
}

void clearTimeSyncedFlag(void)
{
  isTimeSyncedFlag = 0;
}
